﻿using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using MathService;
using MathService.Values;

namespace System
{
    /// <summary>Класс методов расширений для интерфейса перечисления</summary>
    public static class IEnumerableExtentions
    {
        [Pure, Diagnostics.DebuggerStepThrough]
        public static Complex Sum(this IEnumerable<Complex> collection) { return collection.Aggregate((Z, z) => Z + z); }

        //[Pure, Diagnostics.DebuggerStepThrough]
        //public static Complex Sum<T>(this IEnumerable<T> collection, Func<T, Complex> Selector)
        //{
        //    return collection.Select(Selector).Sum();
        //}

        //[Pure, Diagnostics.DebuggerStepThrough]
        //public static Complex Sum<T>(this IEnumerable<T> collection, Func<T, Complex?> Selector)
        //{
        //    return collection.Select(Selector).Where(zz => zz.HasValue).Select(zz => zz.GetValueOrDefault()).Sum();
        //}                                                                                                            

        ///<summary>Проверка перечисления на пустоту</summary>
        ///<param name="collection">Проверяемое перечисление</param>
        ///<typeparam name="T">Тип элемента перечисления</typeparam>
        ///<returns>Истина, если перечисление пусто</returns>
        [Obsolete("Используйте !.Any()")]
        [Pure, Diagnostics.DebuggerStepThrough]
        public static bool IsEmpty<T>(this IEnumerable<T> collection)
        {
            Contract.Requires(collection != null, "Отсутствует ссылка на коллекцию");

            return !collection.Any();

            //using(var e = collection.GetEnumerator())
            //    return !e.MoveNext();
        }

        /// <summary>Объединить элементы коллеции</summary>
        /// <typeparam name="T">Тип элемента коллекции</typeparam>
        /// <typeparam name="TResult">Тип результата</typeparam>
        /// <param name="collection">Исходная коллекция элементов</param>
        /// <param name="Init">Исходное состояние результата объединения</param>
        /// <param name="func">Метод объединения</param>
        /// <param name="index">Индекс элемента коллекции</param>
        /// <returns>Результат объединения коллекции элементов</returns>
        [Pure, Diagnostics.DebuggerStepThrough]
        public static TResult Aggregate<T, TResult>(this IEnumerable<T> collection, TResult Init,
            Func<TResult, T, int, TResult> func, int index = 0)
        {
            Contract.Requires(collection != null, "Отсутствует ссылка на коллекцию");

            return collection.Aggregate(Init, (last, e) => func(last, e, index++));
        }

        /// <summary>Проверка на наличие элемиента в коллекции</summary>
        /// <typeparam name="T">Тип элемента</typeparam>
        /// <param name="collection">Проверяемая коллекция</param>
        /// <param name="selector">Метод выбора</param>
        /// <returns>Истина, если выполняется предикат хотя бы на одном элементе коллекции</returns>
        [Pure, Diagnostics.DebuggerStepThrough]
        public static bool Contains<T>(this IEnumerable<T> collection, Predicate<T> selector)
        {
            Contract.Requires(collection != null, "Отсутствует ссылка на коллекцию");
            Contract.Requires(selector != null, "Отсутствует ссылка на предикат");

            if(collection is List<T>)
            {
                var list = (List<T>)collection;
                for(var i = 0; i < list.Count; i++)
                    if(selector(list[i])) return true;
            }

            if(collection is IList<T>)
            {
                var list = (IList<T>)collection;
                for(var i = 0; i < list.Count; i++)
                    if(selector(list[i])) return true;
            }

            if(collection is T[])
            {
                var list = (T[])collection;
                for(var i = 0; i < list.Length; i++)
                    if(selector(list[i])) return true;
            }

            return collection.Any(t => selector(t));
        }

        /// <summary>Найти элемент в перечислении, удовлетворяющий предикату</summary>
        /// <param name="collection">Перечисление элементов</param>
        /// <param name="selector">Предикат выбора</param>
        /// <typeparam name="T">Тип элементов перечисления</typeparam>
        /// <returns>Найденный элемент, либо пустая ссылка</returns>
        [Pure, Diagnostics.DebuggerStepThrough]
        public static T Find<T>(this IEnumerable<T> collection, Predicate<T> selector)
        {
            Contract.Requires(collection != null, "Отсутствует ссылка на коллекцию");

            foreach(var local in collection.Where(local => selector(local))) return local;
            return default(T);
        }

        ///<summary>Выполнение действия для всех элементов коллекции</summary>
        ///<param name="collection">Коллекция элементов</param>
        ///<param name="Action">Выполняемое действие</param>
        ///<typeparam name="T">Тип элементов коллекции</typeparam>
        [Diagnostics.DebuggerStepThrough]
        public static void Foreach<T>(this IEnumerable<T> collection, Action<T> Action)
        {
            Contract.Requires(collection != null, "Отсутствует ссылка на коллекцию");
            Contract.Requires(Action != null);

            if(collection is IList<T>)
            {
                var list = (IList<T>)collection;
                for(var i = 0; i < list.Count; i++)
                    Action(list[i]);
            }
            else if(collection is IList)
            {
                var list = (IList)collection;
                for(var i = 0; i < list.Count; i++)
                    Action((T)list[i]);
            }
            else
                foreach(var local in collection)
                    Action(local);
        }

        ///<summary>Выполнение действия для всех элементов коллекции с указанием индекса элемента</summary>
        ///<param name="collection">Коллекция элементов</param>
        ///<param name="Action">Действие над элементом</param>
        ///<param name="index">Смещение индекса элемента колеекции</param>
        ///<typeparam name="T">Тип элемента колекции</typeparam>
        [Diagnostics.DebuggerStepThrough]
        public static void Foreach<T>(this IEnumerable<T> collection, Action<T, int> Action, int index = 0)
        {
            Contract.Requires(collection != null, "Отсутствует ссылка на коллекцию");
            Contract.Requires(Action != null);

            if(collection is IList<T>)
            {
                var list = (IList<T>)collection;
                var lv_ElementCount = list.Count;
                for(var i = 0; i < lv_ElementCount; i++)
                    Action(list[i], index++);
            }
            else if(collection is IList)
            {
                var list = (IList)collection;
                var lv_ElementCount = list.Count;
                for(var i = 0; i < lv_ElementCount; i++)
                    Action((T)list[i], index++);
            }
            else
                foreach(var local in collection)
                    Action(local, index++);
        }

        /// <summary>Отложенное выполнение действия до перебора элементов колеекци</summary>
        /// <typeparam name="T">Тип элементов коллеции</typeparam>
        /// <param name="collection">Коллекция элементов</param>
        /// <param name="Action">Выполняемое действие</param>
        /// <returns>Коллекция элементов, для элементов которой которой выполняется действие</returns>
        [Pure, Diagnostics.DebuggerStepThrough]
        public static IEnumerable<T> ForeachLeazy<T>(this IEnumerable<T> collection, Action<T> Action)
        {
            Contract.Requires(collection != null);
            Contract.Ensures(Action == null
                ? Contract.Result<IEnumerable<T>>() == collection
                : Contract.Result<IEnumerable<T>>() != null);

            return Action == null ? collection : collection.Select(t =>
            {
                Action(t);
                return t;
            });
        }

        /// <summary>Отложенное выполнение действия до перебора элементов колеекци</summary>
        /// <typeparam name="T">Тип элементов коллеции</typeparam>
        /// <param name="collection">Коллекция элементов</param>
        /// <param name="Action">Выполняемое действие</param>
        /// <param name="index">Начальный индекс элемента коллекции</param>
        /// <returns>Коллекция элементов, для элементов которой которой выполняется действие</returns>
        [Pure, Diagnostics.DebuggerStepThrough]
        public static IEnumerable<T> ForeachLeazy<T>(this IEnumerable<T> collection, Action<T, int> Action, int index = 0)
        {
            Contract.Requires(collection != null);
            Contract.Ensures(Action == null
                ? Contract.Result<IEnumerable<T>>() == collection
                : Contract.Result<IEnumerable<T>>() != null);
            return Action == null ? collection : collection.Select(t =>
            {
                Action(t, index++);
                return t;
            });
        }

        ///<summary>Вычисление функции от всех элементов колеекции</summary>
        ///<param name="Collection">Коллекция значений аргумента вычисляемой функции</param>
        ///<param name="f">Вычисляемая функция</param>
        ///<typeparam name="T">Тип аргумента функции</typeparam>
        ///<typeparam name="TResult">Тип значения функции</typeparam>
        ///<returns>Коллекция значений функции на всей области определения</returns>
        [Pure, Obsolete("Используйте .Select(Func<T, TResult>)")]
        //[Diagnostics.DebuggerStepThrough]
        public static IEnumerable<TResult> Function<T, TResult>(this IEnumerable<T> Collection, Func<T, TResult> f)
        {
            Contract.Requires(Collection != null);
            Contract.Requires(f != null);
            Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
            return new EnumirableCollectionFunctionCalculator<T, TResult>(Collection, f);
        }

        ///<summary>Класс вычисления значений функции для коллекции аргументов</summary>
        ///<typeparam name="T">Тип аргумента функции</typeparam>
        ///<typeparam name="TResult">Тип значения функции</typeparam>
        private class EnumirableCollectionFunctionCalculator<T, TResult> : IEnumerable<TResult>
        {
            /* ------------------------------------------------------------------------------------------ */

            ///<summary>Перечислитель коллеции рассчитанных значений функции</summary>
            public class EnumirableCollectionFunctionCalculatorEnumirator : IEnumerator<TResult>
            {
                /* ------------------------------------------------------------------------------------------ */

                /// <summary>Перечислитель коллекции аргументов функции</summary>
                private readonly IEnumerator<T> f_Enumirator;

                /// <summary>Вычисляемая функция</summary>
                private readonly Func<T, TResult> f_Function;

                private readonly LazyValue<TResult> f_FunctionValue;

                /* ------------------------------------------------------------------------------------------ */

                /// <summary>Текцущий элемент коллекции значений функции</summary>
                public TResult Current { get { return f_FunctionValue; } }

                /// <summary>Текцущий элемент коллекции значений функции</summary>
                object IEnumerator.Current { get { return Current; } }

                /* ------------------------------------------------------------------------------------------ */

                ///<summary>Инициализация нового перечислителя колеекции рассчитанных значений функции</summary>
                ///<param name="Enumirator">Перечислитель коллекции аргументов области определения</param>
                ///<param name="f">Вычисляемая функция</param>
                public EnumirableCollectionFunctionCalculatorEnumirator(IEnumerator<T> Enumirator, Func<T, TResult> f)
                {
                    Contract.Requires(Enumirator != null);
                    Contract.Requires(f != null);

                    f_Enumirator = Enumirator;
                    f_Function = f;
                    f_FunctionValue = new LazyValue<TResult>(() => f_Function(f_Enumirator.Current));
                }

                /* ------------------------------------------------------------------------------------------ */

                /// <summary>Освобождение ресурсов</summary>
                public void Dispose() { f_Enumirator.Dispose(); }

                /// <summary>Переход к следующему элементу коллекции</summary>
                /// <returns>Истина, если переход выполнен успешно</returns>
                public bool MoveNext()
                {
                    var CanMove = f_Enumirator.MoveNext();
                    if(CanMove) f_FunctionValue.Reset();
                    return CanMove;
                }

                /// <summary>Сброс состояния перечислителя</summary>
                public void Reset()
                {
                    f_Enumirator.Reset();
                    f_FunctionValue.Reset();
                }

                /* ------------------------------------------------------------------------------------------ */
            }

            /* ------------------------------------------------------------------------------------------ */

            /// <summary>Коллекция аргументов функции</summary>
            private readonly IEnumerable<T> f_Collection;

            /// <summary>Вычисляемая функция</summary>
            private readonly Func<T, TResult> f_Function;

            /// <summary>Инициализация нового потокового вычислителя функции на коллекции аргументов</summary>
            /// <param name="Collection">Коллекция аргументов</param>
            /// <param name="f">Вычисляемая функция</param>
            public EnumirableCollectionFunctionCalculator(IEnumerable<T> Collection, Func<T, TResult> f)
            {
                Contract.Requires(Collection != null);
                Contract.Requires(f != null);

                f_Collection = Collection;
                f_Function = f;
            }

            /// <summary>Получение перечислителя</summary>
            /// <returns>Перечислитель рассчитанных значений функции</returns>
            public IEnumerator<TResult> GetEnumerator()
            {
                return new EnumirableCollectionFunctionCalculatorEnumirator(f_Collection.GetEnumerator(), f_Function);
            }

            /// <summary>Неявное получение перечислителя</summary>
            /// <returns>Перечислитель расссчитанных значений функции</returns>
            IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
        }
    }
}

